#pragma once
#include <iostream>
#include <string>
#include <memory>
#include "Socket.hpp"
#include <jsoncpp/json/json.h>
#include <functional>

using namespace SocketModule;

// 实现一个自定义的网络版本的计算器

// 约定好各个字段的含义，本质就是约定好协议！
//  client -> server
// 请求
// 如何要做到序列化和反序列化
// 1.我们直接自己写（怎么做的知道就好，因为不具备很好的扩展性）
// 2.使用现成的方案（我们要写这种）--- json -》 jsoncpp

// content_len（整个json串的长度也要发过去）\r\n jsonstring  \r\n
// 50\r\n{"x":10,"y":20,"oper":'+'}\r\n
// 一开始一直读到\r\n，这样我们读到的内容就一定是完整的json串的长度
// 接下来我们只需要读到该长度大小的报文即为完整的json串
// 也就是说我们的\r\n是用来分离报头（content_len）和有效载荷（jsonstring）的
class Request
{
public:
    Request() {}
    Request(int x, int y, int oper)
        : _x(x),
          _y(y),
          _oper(oper)
    {
    }

    // 提供序列化接口
    std::string Serialize()
    {
        // _x=10  _y=20  _oper='+'
        //"10" "20" '+' ：用空格作为分隔符

        // 使用jsoncpp
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = _oper; //_oper是char类型，也是整数，看到的是对应的ascii值

        Json::FastWriter writer;
        std::string s = writer.write(root);
        return s;
    }

    // 提供反序列化接口
    bool Deserialize(std::string &in)
    {
        //"10" "20" '+' -> 以空格作为分隔符来反序列 -> 10 20 '+'

        // 使用jsoncpp
        Json::Value root;
        Json::Reader reader;
        bool ok = reader.parse(in, root);
        if (ok)
        {
            _x = root["x"].asInt();
            _y = root["y"].asInt();
            _oper = root["oper"].asInt();
        }
        return ok;
    }

    ~Request() {}

    int X() { return _x; }
    int Y() { return _y; }
    char Oper() { return _oper; }

private:
    int _x;
    int _y;
    char _oper; // + - * % -> _x _oper _y -> 10 + 20
};

// server -> client
// 应答
class Response
{
public:
    Response() {}
    Response(int result, int code)
        : _result(result),
          _code(code)
    {
    }

    // 提供序列化接口
    std::string Serialize()
    {
        // 使用jsoncpp
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;

        Json::FastWriter writer;
        std::string s = writer.write(root);
        return s;
    }

    // 提供反序列化接口
    bool Deserialize(std::string &in)
    {
        // 使用jsoncpp
        Json::Value root;
        Json::Reader reader;
        bool ok = reader.parse(in, root);
        _result = root["result"].asInt();
        _code = root["code"].asInt();
        return true;
    }

    // 设置结果方法
    void SetResult(int res)
    {
        _result = res;
    }

    // 设置运算的状态方法
    void SetCode(int code)
    {
        _code = code;
    }

    // 显示结果的方法
    void ShowResult()
    {
        std::cout << "计算结果是：" << _result << "[" << _code << "]" << std::endl;
    }

    ~Response() {}

private:
    int _result; // 运算结果，无法区分清楚应答是计算结果还是异常值
    int _code;   // 0:success,1,2,3,4->不同的运算异常的情况——约定！！就是协议！！
};

const std::string sep = "\r\n";
using func_t = std::function<Response(Request &req)>;
// 协议（基于Tcp）需要解决两个问题
// 1. request和response必须得有序列化和反序列化的功能
// 2. 我们必须得确保读取的时候，读到的是完整的报文（Tcp的数据不一致问题，Udp则没有）
class Protocol
{
public:
    Protocol()
    {
    }

    // 我们把处理计算的业务交给上层来实现
    Protocol(func_t func)
        : _func(func)
    {
    }

    // 编码
    std::string Encode(const std::string jsonstr)
    {
        // 需要对json串进行编码成：50\r\n{"x":10,"y":20,"oper":'+'}\r\n这种形式
        std::string len = std::to_string(jsonstr.size());
        // 做应用层封装报头，编码
        std::string package = len + sep + jsonstr + sep;
        return package;
    }

    // 解码
    // 我们未来会受到类似于50\r\n{"x":10,"y":20,"oper":'+'}这样的字符串
    // 得解码成{"x":10,"y":20,"oper":'+'}这样的完整报文
    // 但是这种是不一定的，我们的tcp面向字节流，可能是残缺的，也可能是有多出来的
    // 比如：残缺：50，多出来：50\r\n{"x":10,"y":20,"oper":'+'}50\r\n
    // 所以我们的Decode方法得完成：
    // 1.判断报文完整性
    // 2.如果包含至少一个完整请求，就得提取它到package中，并从缓冲区中移除它，方便处理下一个
    bool Decode(std::string &buffer, std::string *package)
    {
        // 先从左到右找到第一个分隔符"\r\n"
        ssize_t pos = buffer.find(sep);
        if (pos == std::string::npos) // 说明本次读到的报文连有效长度都不完整
        {
            // 让调用方继续从内核中读取数据
            return false;
        }
        // 走到这就说明当前报文的长度是完整知道的
        // 那就从开头处提取pos个的字符（pos是'\'对应下标，正好可以代表前面有几个字符）
        std::string pack_len_str = buffer.substr(0, pos);
        // 把有效长度转成整数
        int pack_len_int = std::atoi(pack_len_str.c_str());

        // 但是在这里不一定有完整的报文
        // 一个完整字符串的总长度
        int target_len = pack_len_str.size() + pack_len_int + 2 * sep.size();
        // 我们的buffer的长度必须要大于/等于这个target_len才有完整的报文
        if (buffer.size() < target_len)
        {
            // 所以这里的buffer是残缺的，不完整，直接返回false
            return false;
        }

        // 走到这里就说明buffer中至少有一个完整的报文
        // 那我们就开始提取
        // 让buffer从下标为pos+sep.size()的地方分割pack_len_int长度的字符串
        *package = buffer.substr(pos + sep.size(), pack_len_int);
        // 我们的package就是我们提取出来的json串（输出型参数）
        // 然后我们将这一个完整的报文从buffer中移除避免影响后面的读取操作(方便下次解码操作)
        buffer.erase(0, target_len);
        return true;

        // 我们未来调用这个解析解码操作方法时，可以循环调用来达到一直解码操作
    }

    // 获取请求的方法
    void GetRequest(std::shared_ptr<Socket> &sock, InetAddr &client)
    {
        // 读取
        // Recv在不断给这个解析队列+=报文
        // 解析出一个完整报文Recv就将其移除，就相当于一个解析队列啦
        std::string buffer_queue;
        while (true)
        {
            int n = sock->Recv(&buffer_queue); // 把数据读到inbuffer中
            if (n > 0)
            {
                std::string json_package;
                // 1.解析报文，提取完整的json请求，如果不完整，就让服务器继续提取
                // 在这里使用Decode返回进行的循环是为了持续处理挤压的json请求数据
                while (Decode(buffer_queue, &json_package))
                {

                    // 走到这里就能确保拿到了一个完整的报文
                    // 是这样的报文：{"x":10,"y":20,"oper":'+'}
                    // 2.发过来的是请求json串，得做一下反序列化
                    LOG(LogLevel::DEBUG) << client.StringAddr() << " 请求：" << json_package;
                    Request req;
                    bool ok = req.Deserialize(json_package);
                    if (!ok)
                    {
                        // 反序列化失败，继续返回循环再走流程
                        continue;
                    }
                    // 3. 走到这说明反序列化成功了
                    // 一定得到了一个内部属性已经被设置了的req了
                    // 此时我们需要req->resp，要得到_result和_code,也就是要完成计算功能——业务
                    // 这个业务交给上一层完成，我们这里用Response类型变量来接收结果就好
                    Response resp = _func(req);

                    // 4.这个resp应答对象是结构化字符串，我们应该先给它序列化
                    std::string json_str = resp.Serialize();

                    // 5.还要给json_str进行编码(也就是添加自定义长度)形成send_str
                    std::string send_str = Encode(json_str);
                    // 此时这个send_str就是携带长度的应答报文了，比如：50\r\n{}50\r\n

                    // 6.此时就可以将这个send_str写回给客户端了
                    sock->Send(send_str);
                }
            }
            else if (n == 0)
            {
                LOG(LogLevel::INFO) << "client:" << client.StringAddr() << "退出了";
                // 我们也退出
                break;
            }
            else
            {
                LOG(LogLevel::WARNING) << "client" << client.StringAddr() << ",secv error";
                // 读取失败了，退出
                break;
            }
        }
    }

    // 获取应答
    // 把客户端对象、读取存放的缓冲区、应答对象传进来
    bool GetResponse(std::shared_ptr<Socket> &client, std::string &resp_buff, Response *resp)
    {
        // 面向字节流
        // 得先确保client读到得网络字符串是一个完整的应答
        while (true)
        {
            int n = client->Recv(&resp_buff);
            if (n > 0)
            {
                std::cout << "----------resp_buffer-----------" << std::endl;
                std::cout << resp_buff << std::endl;
                std::cout << "---------------------------------" << std::endl;
                // 成功
                std::string json_package;

                // 1.解析报文，提取完整的json应答，如果不完整，就让服务器继续提取
                bool ok = Decode(resp_buff, &json_package);
                if (!ok)
                {
                    continue;
                }
                std::cout << "----------response json-----------" << std::endl;
                std::cout << json_package << std::endl;
                std::cout << "----------------------------------" << std::endl;
                // 走到这里就能确保拿到了一个完整的应答json报文
                // 2.对这个应答做反序列化
                resp->Deserialize(json_package);

                return true;
            }
            else if (n == 0)
            {
                // server端退出了
                std::cout << "server quit" << std::endl;
                return false;
            }
            else
            {
                // 读取失败了
                std::cout << "secv error" << std::endl;
                return false;
            }
        }
    }

    // 客户端构建请求字符串的方法
    std::string BuildRequestString(int x, int y, char oper)
    {
        // 1.构建一个完整的请求
        Request req(x, y, oper);

        // 2.序列化
        std::string json_req = req.Serialize();

        // 2.1 debug
        std::cout << "----------json_req string-----------" << std::endl;
        std::cout << json_req << std::endl;
        std::cout << "--------------------------------------" << std::endl;

        // 3.添加属性报头——编码
        return Encode(json_req);
    }

    ~Protocol() {}

private:
    // 因为我们使用的是多进程
    // Request _req;
    // Response _resp;
    func_t _func;
};